package com.gylang.commons.converter;


import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * DTO和实体类互转
 *
 * @author gylang,
 * date 2020/4/15,
 * @version 1.0
 */
public class ObjectConverter {

    /**
     * copy对象属性
     *
     * @param source   源对象
     * @param <TARGET> 泛型目标对象类
     * @return 返回目标对象list
     */
    public static <TARGET> TARGET objCopy(Object source, TARGET target) {

        BeanUtils.copyProperties(source, target);
        return target;
    }


    /**
     * copy对象属性
     *
     * @param source    源对象
     * @param ignoreArg 忽略属性
     * @param <TARGET>  泛型目标对象类
     * @return 返回目标对象list
     */
    public static <TARGET> TARGET objCopy(Object source, TARGET target, String... ignoreArg) {

        Assert.notNull(source, "实体转换dto失败, 实体不能为空");
        Assert.notNull(target, "带转化dto不能为空");
        BeanUtils.copyProperties(source, target);
        return target;
    }


    /**
     * copy对象属性
     *
     * @param source    源对象
     * @param nullCopy  允许空拷贝
     * @param ignoreArg 忽略属性
     * @param <TARGET>  泛型目标对象类
     * @return 返回目标对象list
     */
    public static <TARGET> TARGET objCopy(Object source, TARGET target, boolean nullCopy, String... ignoreArg) {

        Assert.notNull(source, "实体转换dto失败, 实体不能为空");
        Assert.notNull(target, "带转化dto不能为空");
        Set<String> ignoreSet = new HashSet<>(Arrays.asList(ignoreArg));
        if (nullCopy) {
            BeanUtils.copyProperties(source, target);
        } else {
            ignoreSet.addAll(Arrays.asList(getEmptyArg(source)));
            BeanUtils.copyProperties(source, target, getEmptyArg(ignoreSet.toArray()));

        }
        return target;
    }

    /**
     * copy对象属性
     *
     * @param source   源对象
     * @param nullCopy 允许空拷贝
     * @param <TARGET> 泛型目标对象类
     * @return 返回目标对象list
     */
    public static <TARGET> TARGET objCopy(Object source, TARGET target, boolean nullCopy) {

        Assert.notNull(source, "实体转换dto失败, 实体不能为空");
        Assert.notNull(target, "带转化dto不能为空");
        if (nullCopy) {
            BeanUtils.copyProperties(source, target);
        } else {
            BeanUtils.copyProperties(source, target, getEmptyArg(source));
        }
        return target;
    }

    /**
     * copy对象属性
     *
     * @param source      源对象
     * @param targetClass 目标对象类
     * @param nullCopy    允许空拷贝
     * @param <TARGET>    泛型目标对象类
     * @return 返回目标对象list
     */
    public static <SOURCE, TARGET> List<TARGET> listCopy(List<SOURCE> source, Class<TARGET> targetClass, boolean nullCopy) {

        List<TARGET> targetList = null;


        targetList = source.stream().map(source1 -> {
            TARGET target1 = null;
            try {
                target1 = targetClass.newInstance();
                ObjectConverter.objCopy(source1, target1, nullCopy);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return target1;
        }).collect(Collectors.toList());

        return targetList;
    }

    /**
     * copy对象属性
     *
     * @param source      源对象
     * @param targetClass 目标对象类
     * @param <TARGET>    泛型目标对象类
     * @return 返回目标对象list
     */
    public static <SOURCE, TARGET> List<TARGET> listCopy(List<SOURCE> source, Class<TARGET> targetClass) {

        List<TARGET> targetList = null;


        targetList = source.stream().map(source1 -> {
            TARGET target1 = null;
            try {
                target1 = targetClass.newInstance();
                ObjectConverter.objCopy(source1, target1);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return target1;
        }).collect(Collectors.toList());

        return targetList;
    }

    /**
     * copy对象属性
     *
     * @param source      源对象
     * @param targetClass 目标对象类
     * @param nullCopy    允许空拷贝
     * @param ignoreArg   忽略属性
     * @param <TARGET>    泛型目标对象类
     * @return 返回目标对象list
     */
    public static <SOURCE, TARGET> List<TARGET> listCopy(List<SOURCE> source, Class<TARGET> targetClass, boolean nullCopy, String[] ignoreArg) {

        List<TARGET> targetList = null;

        targetList = source.stream().map(source1 -> {
            TARGET target1 = null;
            try {
                target1 = targetClass.newInstance();
                ObjectConverter.objCopy(source1, target1, nullCopy, ignoreArg);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return target1;
        }).collect(Collectors.toList());

        return targetList;
    }


    /**
     * 获取空属性名
     *
     * @param source 源对象
     * @return 返回空值属性名 list
     */
    private static String[] getEmptyArg(Object source) {

        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (isEmpty(srcValue)) {

                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    private static boolean isEmpty(Object obj) {
        if (null != obj) {
            if (obj instanceof String) {
                return StringUtils.isEmpty(obj);
            } else if (obj instanceof Collection) {
                return ((Collection<?>) obj).isEmpty();
            } else if (obj instanceof Map) {
                return ((Map<?, ?>) obj).isEmpty();
            } else {
                return false;
            }

        }
        return true;
    }

    /**
     * 类型转换
     *
     * @param source 源
     * @param target 目标
     * @param <T>    转换后的类型
     * @return 转换后的对象
     */
    public static <T> T converter(Object source, Class<T> target) {

        if (null != source && null != target
                && source.getClass().getCanonicalName().equals(target.getCanonicalName())) {
            return (T) source;
        }
        return null;
    }

    /**
     * 类型转换
     *
     * @param source 源
     * @param target 目标
     * @param <T>    转换后的类型
     * @return 转换后的对象
     */
    public static <T> T converter(Object source, T target) {

        if (null != source && null != target
                && source.getClass().getCanonicalName().equals(target.getClass().getCanonicalName())) {
            return (T) source;
        }
        return null;
    }

    /**
     * 将map 属性copy到对象当中
     *
     * @param map   map
     * @param clazz 目标类
     * @param <T>   返回
     * @return
     */
    public static <T> T map2Obj(Map<String, ?> map, Class<T> clazz) {

        try {
            T t = clazz.newInstance();
            map2Obj(map, t);
            return t;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将map 属性copy到对象当中
     *
     * @param map map
     * @param obj 目标对象
     * @param <T> 返回
     * @return
     */
    public static <T> void map2Obj(Map<String, ?> map, Object obj) {

        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object o = map.get(field.getName());
            if (field.getType().isInstance(o)) {

                Method method = null;
                try {
                    method = obj.getClass()
                            .getMethod(setterMethod(field.getName()), field.getType());
                    if (null != method) {
                        method.invoke(obj, o);
                    }
                } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    private static final String set = "set";

    private static String setterMethod(String fieldName) {

        return set + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    public static void main(String[] args) {
        System.out.println(setterMethod("receive"));
    }

}
